package fag.core.game;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import javax.swing.SwingUtilities;
import javax.swing.event.EventListenerList;

/**
 * Singleton du gestionnaire de ressources. Il s'occupe de charger, partager et décharger les ressources.
 * @author lumz
 */
public class ResourceManager {

	/**
	 * Classe représentant un chargement.
	 * @author lumz
	 */
	public abstract class Loading extends Thread implements Runnable {
		
		private final EventListenerList listeners = new EventListenerList();
		
		private int progress = 0;
		
		/**
		 * Constructeur par défaut.
		 */
		public Loading() {
		}
		
		/**
		 * Ajouter un écouteur de changement de propriété.
		 * @param listener L'écouteur à ajouter.
		 */
		public void addPropertyChangeListener(PropertyChangeListener listener) {
			listeners.add(PropertyChangeListener.class, listener);
		}
		
		/**
		 * Travail à effectuer en parallèle.
		 * @return Le résultat du chargement est rendu disponible.
		 */
		public abstract Map<String, Object> load();
		
		/**
		 * Retirer un écouteur de changement de propriété.
		 * @param listener L'écouteur à retirer.
		 */
		public void removePropertyChangeListener(PropertyChangeListener listener) {
			listeners.remove(PropertyChangeListener.class, listener);
		}
		
		/**
		 * Lanceur du thread parallèle.
		 */
		public void run() {
			pool.putAll(load());
		}
		
		/**
		 * Avertir un changement de propriété.
		 * @param propertyName Nom de la propriété qui change.
		 * @param oldValue L'ancienne valeur.
		 * @param newValue La nouvelle valeur.
		 */
		protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
			
			for(PropertyChangeListener listener : listeners.getListeners(PropertyChangeListener.class)) {
				
				SwingUtilities.invokeLater(new Runnable() {

					private PropertyChangeListener listener = null;
					
					private String propertyName = "";
					
					private Object oldValue = 0;
					private Object newValue = 0;
					
					@Override
					public void run() {
						listener.propertyChange(new PropertyChangeEvent(this, propertyName, oldValue, newValue));
					}
					
					/**
					 * 
					 * @param listener
					 * @param oldProgress
					 * @param newProgress
					 * @return
					 */
					public Runnable setup(PropertyChangeListener listener, String propertyName, Object oldValue, Object newValue) {
						
						this.listener = listener;
						this.propertyName = propertyName;
						this.oldValue = oldValue;
						this.newValue = newValue;
						
						return this;
					}
					
				}.setup(listener, propertyName, oldValue, newValue));
			}
		}
	}
	
	private static ResourceManager instance = null;
	
	private Map<String, Object> pool = Collections.synchronizedMap(new HashMap<String, Object>());
	
	/**
	 * Constructeur par défault.
	 */
	private ResourceManager() {}
	
	/**
	 * Accesseur de l'instance du gestionnaire de ressources.
	 * @return L'instance singleton du bac à modèles.
	 */
	public static ResourceManager get() {
		
		if(instance == null)
			instance = new ResourceManager();
		
		return instance;
	}
	
	/**
	 * Obtenir une référence vers une ressource.
	 * @param name Nom de la ressource à partager.
	 * @return La ressource à partager ou null si il n'existe aucune ressource portant ce nom.
	 */
	public Object acquire(String name) {
		return pool.get(name);
	}
	
	/**
	 * Enregistrer une ressource.
	 * Les noms de ressource sont uniques. Si une ressource est enregistré alors que le nom
	 * est déjà utilisé, rien ne se passe.
	 * @param name Nom de la ressource à enregistrer.
	 * @parma resource Object à enregistrer.
	 */
	public void register(String name, Object resource) {
		pool.put(name, resource);
	}
	
	/**
	 * Libérer une ressource.
	 * @param name Nom de la ressource à libérer.
	 */
	public void release(String name) {
		pool.remove(name);
	}
}
